﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Threading;
using LogLib;
using ComLib.ComType;
using System.IO;

namespace DYPSensor
{
    public delegate void SensorReadDataReceivedEventHandler(SensorReadResult result);

    public delegate void LadarErrorReceivedEventHandler(string error, int code);

    /// <summary>
    /// 传感器通信服务
    /// </summary>
    public class DYPSensorService
    {
        /// <summary>
        /// 错误信息
        /// </summary>
        private string _error = "";
        public string Error
        {
            get { return _error; }
        }

        /// <summary>
        /// 读取探测距离实时值
        /// </summary>
        public byte[] CmdReadDistance = new byte[] { 0x55, 0xaa, 0x01, 0x01, 0x01 }; 
        
        /// <summary>
        /// 读取探测距离过滤值
        /// </summary>
        public byte[] CmdReadDistanceFilterValue = new byte[] { 0x55, 0xaa, 0x01, 0x05, 0x05 }; 

        /// <summary>
        /// 读取温度
        /// </summary>
        public byte[] CmdReadTemperature = new byte[] { 0x55, 0xaa, 0x01, 0x02, 0x02 }; 

        /// <summary>
        /// 修改地址
        /// </summary>
        public byte[] CmdModifyAdress = new byte[] { 0x55, 0xaa, 0x01, 0x03, 0x07 }; //停止转动

        public SensorReadType ReadType { get; set; }

        private DYPSensorOption _option = new DYPSensorOption();
        public DYPSensorOption Option
        {
            get { return _option; }
        }

        public event SensorReadDataReceivedEventHandler dataReceived;
        private void OnMeasuringUpdated(SensorReadResult result)
        {
            if (dataReceived != null)
            {
                dataReceived(result);
            }
        }

        public event LadarErrorReceivedEventHandler errorReceived;
        private void OnErrorReceived(string error, int code)
        {
            if (errorReceived != null)
            {
                errorReceived(error, code);
            }
        }

        public SerialPort serialPort;

        /// <summary>
        /// 判断串口是否打开
        /// </summary>
        public bool IsOpen
        {
            get
            {
                if (serialPort != null)
                {
                    return serialPort.IsOpen;
                }
                return false;
            }
        }

        private Thread thread = null;

        private bool _isMeasuring = false;
        public bool IsWorking
        {
            get { return _isMeasuring; }
        }
   
        /// <summary>
        /// 等待处理事件
        /// </summary>
        protected ManualResetEventSlim _waitEvent;

        public DYPSensorService()
        {
            if (!File.Exists(_option.ConfigFilePath))
            {
                _option.Save();
            }         
            _option.Load();
            _waitEvent = new ManualResetEventSlim(false);
            serialPort = new SerialPort();
            serialPort.DataReceived += OnDataReceived;
            ReadType = SensorReadType.Distance;
        }

        private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SensorReadResult result = null;
            try
            {
                int count = serialPort.BytesToRead;
                if (count > 0)
                {
                    byte[] readBuffer = new byte[count];
                    serialPort.Read(readBuffer, 0, count);                  
                    Log.WriteAsync($"Receive data form senor: { byteToHexStr(readBuffer)}");
                    if (TryParseMeasuredResult(readBuffer, out result))
                    {
                        OnMeasuringUpdated(result);
                    }                    
                }
            }
            catch
            { 
            }     
        }

        /// <summary>
        /// 字节数组转16进制字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string byteToHexStr(byte[] bytes)
        {
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2");
                    returnStr += " ";
                }
            }
            return returnStr;
        }

        public bool Open()
        {
            _error = "";          
            serialPort.BaudRate = _option.BaudRate; //9600
            serialPort.DataBits = _option.DataBit;
            serialPort.StopBits = _option.StopBit;
            serialPort.Parity = _option.Parity;
            serialPort.ReadTimeout = _option.ReadTimeout;
            serialPort.PortName = _option.ComNo;
            bool ret = true;
            try
            {
                serialPort.Open();
            }
            catch (Exception ex)
            {
                _error = ex.Message;
                Log.WriteAsync(ex.ToString());
                return false;
            }

            if (serialPort.IsOpen)
            {
                Log.WriteAsync("串口打开成功!");
            }
            else
            {
                _error = "串口打开失败!";
                Log.WriteAsync(_error);
                ret = false;
            }
            return ret;         
        }

        public void Close()
        {
            _isMeasuring = false;
            SetEvent();
            serialPort.DataReceived += OnDataReceived;
            if (serialPort.IsOpen)
            {
                dataReceived = null;
                try
                {
                    serialPort.Close();
                }
                catch (Exception ex)
                {
                    Log.WriteException(ex.ToString());
                }                
            }          
        }

        /// <summary>
        /// 读取测量距离实时值
        /// </summary>
        public void ReadDistance()
        {
            serialPort.Write(CmdReadDistance, 0, CmdReadDistance.Length);
        }

        /// <summary>
        /// 读取测量距离过滤值
        /// </summary>
        public void ReadDistanceFilterValue()
        {
            serialPort.Write(CmdReadDistanceFilterValue, 0, CmdReadDistanceFilterValue.Length);
        }

        /// <summary>
        /// 读取温度
        /// </summary>
        public void ReadTemperature()
        {
            serialPort.Write(CmdReadTemperature, 0, CmdReadTemperature.Length);
        }

        /// <summary>
        /// 修改地址
        /// </summary>
        public void ModifyAdress()
        {
            serialPort.Write(CmdModifyAdress, 0, CmdModifyAdress.Length);
        }

        public void StartWorking()
        {
            _isMeasuring = true;
            thread = new Thread(OnMeasuring);
            thread.Start();
            SetEvent();
        }

        public void StopWorking()
        {
            _isMeasuring = false;
            SetEvent();
        }

        /// <summary>
        /// 使等待事件获得信号, 恢复被暂停/阻塞的工作项任务
        /// </summary>
        public void SetEvent()
        {
            _waitEvent.Set();
        }

        /// <summary>
        /// 暂停/阻塞任务
        /// </summary>
        public void Wait()
        {
            _waitEvent.Reset();
        }

        private void OnMeasuring()
        {
            Log.WriteAsync("开始连续测量...");
            while (_isMeasuring)
            {
                _waitEvent.Wait();
                if (!_isMeasuring)
                    break;
                if (!serialPort.IsOpen)
                {
                    Log.WriteAsync("检测到串口已经被关闭，测量服务将被终止.");
                    break;
                }                                       
                try
                {
                    switch (ReadType)
                    {
                        case SensorReadType.Distance:
                            ReadDistance();
                            break;
                        case SensorReadType.DistanceFilterValue:
                            ReadDistanceFilterValue();
                            break;
                        case SensorReadType.Temperature:
                            ReadTemperature();
                            break;
                        case SensorReadType.All:
                            ReadDistance();
                            Thread.Sleep(100);
                            ReadDistanceFilterValue();
                            Thread.Sleep(100);
                            ReadTemperature();
                            break;
                        default:
                            break;
                    }                  
                }
                catch(Exception ex)
                {
                    Log.WriteException(ex.ToString());
                    continue;
                }               
                Thread.Sleep(_option.ScanInterval);
            }
            Log.WriteAsync("停止连续测量...");
        }

        private bool TryParseMeasuredResult(byte[] buffer, out SensorReadResult result)
        {
            result = new SensorReadResult();
            if (buffer == null || buffer.Length == 0)
            {
                Log.WriteAsync("读取结果为空...");
                return false;
            }
            if(buffer.Length != 7)
            {
                Log.WriteAsync("读取结果长度不正确.");
                return false;
            }
            if (buffer[0] != 0x55 || buffer[1] != 0xaa)
            {
                Log.WriteAsync("读取结果帧头不正确");
                return false;
            }
            if (buffer[3] == 0x01)
            {
                result.ReadType = SensorReadType.Distance;
                result.ReadValue = (float)GetReadValue(buffer) / 10;
                return true;
            }
            if (buffer[3] == 0x05)
            {
                result.ReadType = SensorReadType.DistanceFilterValue;
                result.ReadValue = (float)GetReadValue(buffer) / 10.0F;
                return true;
            }
            if (buffer[3] == 0x02)
            {
                result.ReadType = SensorReadType.Temperature;
                int value = GetReadValue(buffer);
                int pos = get_bit_high_pos(value);
                if (pos == 16)
                {
                    result.ReadValue = -GetSubZeroTemperature(value) / 10.0F;
                }
                else
                {
                    result.ReadValue = GetSubZeroTemperature(value) / 10.0F;
                }
                return true;
            }
            else
            {
                Log.WriteAsync($"无法解析读取结果, 位置指令{buffer[4]}");
                return false;
            }
        }

        private int get_bit_high_pos(int n)
        {
            int num = Math.Abs(n);
            int i = 0, pos = 0;
            for (i = 0; i <= 8 * sizeof(int); i++, num >>= 1)
            {
                if ((num & 1) == 1)
                {
                    pos = i + 1;
                }
            }
            return pos;
        }

        private int GetSubZeroTemperature(int value)
        {
            return value & 0x7FFF;
        }

        private int GetReadValue(byte[] buffer)
        {
            int hign = buffer[4];
            int low = buffer[5];
            int temp = hign << 8;
            int result = (temp | low);
            return result;
        }    
    }
}
